#simple script to compare two IQ files
# the diff will be output in a xml file
# a simple mode will verify only changes or removal regarding the reference file
param ([string] $file1, [string] $file2, [string] $outputFile, [switch] $simple,[switch] $debug, [switch] $help) 

function usage() {
	Write-Host "IQDiff.ps1 -file1 <reference filename> -file2 <filename> -outputFile <Absolutefilename> [-simple] [-debug] [-help]" 
	Write-Host "-simple : The simple mode, will only check modification or removal relative to data in file1"
}

function fail( [string] $message) {
	Write-Host "the program encounter an error : " 
	Write-Host "$message"
	usage
	exit 1 ;
}

Write-Host "	<IQDiff.ps1>  Copyright (C) 2012  Laurent.Tardif (laurent.tardif@persistentsas.com)"
Write-Host ""
Write-Host "    This program comes with ABSOLUTELY NO WARRANTY; for details see the copyrith.txt file."
Write-Host "    This is free software, and you are welcome to redistribute it"
Write-Host "    under certain conditions; Read the copyrith.txt file for details."


if([String]::IsNullOrEmpty($file1))
{
		fail "The file1 element was not provided as parameter"
} 

if([String]::IsNullOrEmpty($file2))
{
		fail "The file2 element was not provided as parameter"
} 

if([String]::IsNullOrEmpty($outputFile))
{
		fail "The outputFile element was not provided as parameter"
} 

#<IQCheck>
#  <META>
#    <Date>07/11/2011 17:00:46</Date>
#    <Version>1.14</Version>
#    <IP>10.190.200.133</IP>
#    <Hostname>Hudson-w7-32-01</Hostname>
#  </META>
# ...
function writeMeta ([System.Xml.Linq.XElement]  $meta1 ,  [System.Xml.Linq.XElement] $meta2){
	if ($debug) { Write-Host  "-----------------------------------------------------------------------"}
	if ($debug) { Write-Host "Writing meta section " }
	if ($simple) {
		Write-Output "   <meta mode='simple'>" | out-file $outputFile -enc ascii -append
	} else {
		Write-Output "   <meta>" | out-file $outputFile -enc ascii -append
	}	
	$date=$meta1.Element("Date").Value
	$Version=$meta1.Element("Version").Value
	$IP=$meta1.Element("IP").Value
	$hostname=$meta1.Element("Hostname").Value
	Write-Output "     <file1>" | out-file $outputFile -enc ascii -append
	Write-Output "       <Date>$date</Date>" | out-file -enc ascii $outputFile -append
	Write-Output "       <Version>$Version</Version>" | out-file -enc ascii $outputFile -append
	Write-Output "       <IP>$IP</IP>" | out-file -enc ascii $outputFile -append
	Write-Output "       <Hostname>$hostname</Hostname>" | out-file -enc ascii $outputFile -append
	Write-Output "     </file1>" | out-file $outputFile -enc ascii -append
	
	$date=$meta2.Element("Date").Value
	$Version=$meta2.Element("Version").Value
	$IP=$meta2.Element("IP").Value
	$hostname=$meta2.Element("Hostname").Value
	Write-Output "     <file2>" | out-file $outputFile -enc ascii -append
	Write-Output "       <Date>$date</Date>" | out-file -enc ascii $outputFile -append
	Write-Output "       <Version>$Version</Version>" | out-file -enc ascii $outputFile -append
	Write-Output "       <IP>$IP</IP>" | out-file -enc ascii $outputFile -append
	Write-Output "       <Hostname>$hostname</Hostname>" | out-file -enc ascii $outputFile -append
	Write-Output "     </file2>" | out-file $outputFile -enc ascii -append
	
	Write-Output "   </meta>" | out-file $outputFile -enc ascii -append

	
	
}



# #######################################################################
#
# convert a Operating system list to a hash
#
# #######################################################################

#  <OperatingSystem>
#    <Name>Microsoft Windows 7 Professional |C:\Windows|\Device\Harddisk0\Partition2</Name>
#    <Caption>Microsoft Windows 7 Professional </Caption>
#    <OSArchitecture>32-bit</OSArchitecture>
#    <OSType>18</OSType>
#    <SystemDirectory>C:\Windows\system32</SystemDirectory>
#    <Version>6.1.7600</Version>
#    <BuildNumber>7600</BuildNumber>
#    <ServicePackMajorVersion>0</ServicePackMajorVersion>
#    <ServicePackMinorVersion>0</ServicePackMinorVersion>
#    <RegisteredUser>Open Source Grenoble</RegisteredUser>
#    <Organization></Organization>
#    <SerialNumber>55041-050-1231236-86707</SerialNumber>
#    <OSLanguage>1033</OSLanguage>
#    <MUILanguages>en-US</MUILanguages>
#    <Locale>0409</Locale>
#    <CountryCode>1</CountryCode>
#  </OperatingSystem>
function convertOperatingSystemToHash ( [System.Xml.Linq.XElement] $OperatingSystem) {
	$myHash=@{}
	foreach ($information in $OperatingSystem.Elements() ) {
		$name=$information.Name
		$version=$information.Value
		if([String]::IsNullOrEmpty($version)) {
			$version="NULL_VALUE"
		}
		if ($debug) { Write-Host  " find information ( $name), $version)" }
		$myHash.$name=$version
	}
	return $myHash
}

# #######################################################################
#
# convert a ServerId to a hash
#
# #######################################################################

#  <ServerId>
#    <Name>FRFONW0001300</Name>
#    <Domain>PERSISTENTSAS.COM</Domain>
#  </ServerId>
function convertServerIdToHash ( [System.Xml.Linq.XElement] $ServerId) {
	$myHash=@{}
	foreach ($information in $ServerId.Elements() ) {
		$name=$information.Name
		$value=$information.Value
		if([String]::IsNullOrEmpty($value)) {
			$value="NULL_VALUE"
		}
		if ($debug) { Write-Host  " find information ($name), ($value)" }
		$myHash.$name=$value
	}
	return $myHash
}
   

# #######################################################################
#
# convert a software list to a hash
#
# #######################################################################

# <IQCheck>
#   <Softwares length="69">
#    <Software>
#    		 <name>Microsoft Visual Studio 2010 ADO.NET Entity Framework Tools</name>
#    		 <version>10.0.30319</version>
#     </Software>
#   </Softwares>

function convertSoftwaresToHash ( [System.Xml.Linq.XElement] $softwares) {
	$myHash=@{}
	foreach ($software in $softwares.Elements() ) {
		$name=$software.Element("name").Value
		$version=$software.Element("version").Value
		if([String]::IsNullOrEmpty($version)) {
			$version="NULL_VALUE"
		}
		if ($debug) { Write-Host  " find software ( $name), $version)" }
		$myHash.$name=$version
	}
	return $myHash
}


# #######################################################################
#
# convert a Services list to a hash
#
# #######################################################################

#<Services length="156">
#    <Service>
#    		<name>ActiveX Installer (AxInstSV)</name>
#    		<status>Stopped</status>
#    </Service>
 
function convertServicesToHash ( [System.Xml.Linq.XElement] $Services) {
	$myHash=@{}
	foreach ($Service in $Services.Elements() ) {
		$name=$Service.Element("name").Value
		$status=$Service.Element("status").Value
		if([String]::IsNullOrEmpty($status)) {
			$status="NULL_VALUE"
		}
		
		if ($debug) { Write-Host  " find Services ( $name), $status)" }
		$myHash.$name=$status
	}
	return $myHash
}

# #######################################################################
#
# convert a Hotfixes list to a hash
#
# #######################################################################

#<hotfixes length="156">
#    <hotfixe>
#    		<name>K687654</name>
#    		<version>Updated</version>
#    </hotfixe>
 
function convertHotFixesToHash ( [System.Xml.Linq.XElement] $Hotfixes) {
	$myHash=@{}
	foreach ($Hotfixe in $Hotfixes.Elements() ) {
		$name=$Hotfixe.Element("name").Value
		$version=$Hotfixe.Element("version").Value
		if ($debug) { Write-Host  " find $Hotfixes ( $name), $version)" }
		$myHash.$name=$version
	}
	return $myHash
}


# #######################################################################
#
# convert a disks list (child of hardware) to a hash
#
# #######################################################################

#<hardware>
#	<memory>C:Windows</memory>
#	<cpu>2</cpu>
#   <disks>
#    	<name>C:Windows</name>
#   	<size> 100 GB</size>
#   </disks>
#</hardware>
 
function convertHardwareToHashDisks ( [System.Xml.Linq.XElement] $Hardwares) {
	$disksXml=$Hardwares.Element("disks")
	$myHash=@{}
	foreach ($hardware in $disksXml.Elements() ) {
		$name=$hardware.Element("name").Value
		$size=$hardware.Element("size").Value
		if ($debug) { Write-Host  " find $hardwares ($name), $size)" }
		$myHash.$name=$size
	}
	return $myHash
}


# #######################################################################
#
# Convert a value element (contains only one value field) to a hash
#
# #######################################################################
  
function convertValueElementToHash ( [System.Xml.Linq.XElement] $Hardwares, [String] $section) {
	$sectionXml=$Hardwares.Element($section)

    $myHashOneValue=@{}
    if(-not([String]::IsNullOrEmpty($sectionXml))) {
    	$value=$sectionXml.Value
    	$myHashOneValue[$section]=$value
    }

	return $myHashOneValue
}

# #######################################################################
#
# Convert an Environment Variable list to a hash
#
# #######################################################################
  
#  <Environment length="36">
#    <Variable>
#    		<name>ALLUSERSPROFILE</name>
#    		<value>C:\ProgramData</value>
#    </Variable>
 
function convertEnvironmentToHash ( [System.Xml.Linq.XElement] $EnvironmentVariables) {
	$myHash=@{}
	foreach ($EnvironmentVariable in $EnvironmentVariables.Elements() ) {
		$name=$EnvironmentVariable.Element("name").Value
		$value=$EnvironmentVariable.Element("value").Value
		if([String]::IsNullOrEmpty($value)) {
			$value="NULL_VALUE"
		}		
		if ($debug) { Write-Host  " find EnvironmentVariable ( $name), $value)" }
		$myHash.$name=$value
	}
	return $myHash
}

# #######################################################################
#
# Compute the IQ Diff between 2 hash table
#
# #######################################################################

function genericDiff (  [hashtable] $param1,  [hashtable] $param2, [String] $element,
						[String] $indentInFile, [String] $writeAfterElement) {
	Write-Output "$indentInFile<$($element)$writeAfterElement>" | out-file $outputFile -enc ascii -append

	foreach ($key in $param1.keys) {
		if ($debug) {  Write-Host "checking if ($key) exist in param2" }
		if([String]::IsNullOrEmpty($param2.$key))
		{
			if ($debug) {  Write-Host "One more not found : $key" }
			$encodedkey=[system.web.httputility]::htmlencode($key)
			$encodedvalue=[system.web.httputility]::htmlencode($($param1.$key))
			Write-Output "$indentInFile  <missing_$element>$encodedkey with value $encodedvalue</missing_$element> " | out-file $outputFile -enc ascii -append
		} else {
			#Check if version are identical
			if ($param2.$key -eq $param1.$key) {
				if ($debug) { Write-Host "version are equals ( $($param2.$key) - $($param1.$key) )" }
			} else {
				$encodedkey=[system.web.httputility]::htmlencode($key)
				$encodedvalue=[system.web.httputility]::htmlencode($($param1.$key))
				$encodedvalue2=[system.web.httputility]::htmlencode($($param2.$key))
				Write-Output "$indentInFile  <different_$element>$encodedvalue2 instead of $encodedvalue for $encodedkey </different_$element>" | out-file $outputFile -enc ascii -append	
				if ($debug) { Write-Host "version are not equals ( $($param2.$key) - $($param1.$key) )"  }
			} #end if version
			#remove the key to allow check off additional soft in list2
			$param2.remove($key);
		} #end if software
	} # end foreach param1
	
	#In simple mode we do not display additional info
	if (-not $simple) {
		foreach ($key in $param2.keys) {
			if ($debug) {Write-Host "The key doesn't exist in the reference file $key"}
			$encodedkey=[system.web.httputility]::htmlencode($key)
			$encodedvalue2=[system.web.httputility]::htmlencode($($param2.$key))
			Write-Output "$indentInFile  <additional_$element>$encodedkey with value $encodedvalue2</additional_$element> " | out-file $outputFile -enc ascii -append
		} # end foreach param2
	}
	Write-Output "$indentInFile</$($element)$writeAfterElement>" | out-file $outputFile -enc ascii -append

}

# #######################################################################
#
# Compute the IQ Diff between software list
#
# #######################################################################

function computeSoftDiff (  [hashtable] $param1,  [hashtable] $param2) {
	genericDiff $param1 $param2 "software" " " "s"
}

# #######################################################################
#
# Compute the IQ Diff between Services list
#
# #######################################################################

#<Services length="156">
#    <Service>
#    		<name>ActiveX Installer (AxInstSV)</name>
#    		<status>Stopped</status>
#    </Service>
function computeServicesDiff (  [hashtable] $Services1,  [hashtable] $Services2) {
	genericDiff $Services1 $Services2 "Service" " " "s"
}  
  
# #######################################################################
#
# Compute the IQ Diff between HotFixes list
#
# #######################################################################

#<hotfixes length="156">
#    <hotfixe>
#    		<name>ActiveX Installer (AxInstSV)</name>
#    		<status>Stopped</status>
#    </hotfixe>
function computeHotFixesDiff (  [hashtable] $hotfixes1,  [hashtable] $hotfixes2) {
	genericDiff $hotfixes1 $hotfixes2 "hotfixe" " " "s"
}  
  
# #######################################################################
#
# Compute the IQ Diff between Hardware list
#
# #######################################################################

#<hardware>
#	<memory>4000 MB</memory>
#	<cpu>2</cpu>
#   <disks>
#    	<name>C:Windows</name>
#   	<size> 100 GB</size>
#   </disks>
#</hardware>
function computeHardwareDiff (  [System.Xml.Linq.XElement] $hashHardware1,  [System.Xml.Linq.XElement] $hashHardware2,
							    [hashtable] $hashMemory1,  [hashtable] $hashMemory2,
								[hashtable] $hashCpu1,  [hashtable] $hashCpu2,
								[hashtable] $hashDisks1,  [hashtable] $hashDisks2) {
	Write-Output " <hardware>" | out-file $outputFile -enc ascii -append
	
    $IsNullHardware1 = [String]::IsNullOrEmpty($hardware1)
    $IsNullHardware2 = [String]::IsNullOrEmpty($hardware2)
	if($IsNullHardware1 -and $IsNullHardware2) {
			# The 2 elements do not exist => nothing to compare
	}
	elseif($IsNullHardware1 -and (-not $IsNullHardware2)) {
		if (-not $simple) {
		   #only hashHardware1 does not exist
		   Write-Output "   The file ($file1) does not contain an hardware section" | out-file $outputFile -enc ascii -append
	   }
	}
	elseif ($IsNullHardware2 -and (-not $IsNullHardware1)) {
		#only hashHardware2 does not exist
		Write-Output "   The file ($file2) does not contain an hardware section" | out-file $outputFile -enc ascii -append
	}
	else {
		$indent = "    "
		genericDiff $hashMemory1 $hashMemory2 "memory" $indent ""
		genericDiff $hashCpu1 $hashCpu2 "cpu" $indent ""
		genericDiff $hashDisks1 $hashDisks2 "disk" $indent "s"
	}
	
	Write-Output " </hardware>" | out-file $outputFile -enc ascii -append
}  
  
# #######################################################################
#
# Compute the IQ Diff between Environment Variable list
#
# #######################################################################
  
#  <Environment length="36">
#    <Variable>
#    		<name>ALLUSERSPROFILE</name>
#    		<value>C:\ProgramData</value>
#    </Variable>
function computeEnvironmentDiff (  [hashtable] $Environment1,  [hashtable] $Environment2) {
	genericDiff $Environment1 $Environment2 "Environment" " " "s"
}

# #######################################################################
#
# Compute the IQ Diff between Operating system list
#
# #######################################################################
#  <OperatingSystem>
#    <Name>Microsoft Windows 7 Professional |C:\Windows|\Device\Harddisk0\Partition2</Name>
#    <Caption>Microsoft Windows 7 Professional </Caption>
#    <OSArchitecture>32-bit</OSArchitecture>
#    <OSType>18</OSType>
#    <SystemDirectory>C:\Windows\system32</SystemDirectory>
#    <Version>6.1.7600</Version>
#    <BuildNumber>7600</BuildNumber>
#    <ServicePackMajorVersion>0</ServicePackMajorVersion>
#    <ServicePackMinorVersion>0</ServicePackMinorVersion>
#    <RegisteredUser>OpenSource Grenoble</RegisteredUser>
#    <Organization></Organization>
#    <SerialNumber>55041-050-123-86707</SerialNumber>
#    <OSLanguage>1033</OSLanguage>
#    <MUILanguages>en-US</MUILanguages>
#    <Locale>0409</Locale>
#    <CountryCode>1</CountryCode>
#  </OperatingSystem>
  
function computeOperatingSystemDiff (  [hashtable] $OperatingSystem1,  [hashtable] $OperatingSystem2) {
	genericDiff $OperatingSystem1 $OperatingSystem2 "OperatingSystem" " " "s"
}

# #######################################################################
#
# Compute the IQ Diff between serverId list
#
# #######################################################################
#  <serverId>
#    <Name>FRFONWOOO300</Name>
#    <Domain>PERSISTENTSAS.COM</Domain>
#  </serverId>
  
function computeServerIdDiff (  [hashtable] $ServerId1,  [hashtable] $ServerId2) {
	genericDiff $ServerId1 $ServerId2 "serverId" " " ""
}

# <directory> 
#    <attributes><Name>C:\Development\Caddying\CaddyPowerShell\2_IQToolsExample\data\dirFirst\plugins\*\META-INF\</Name><pattern>MANIFEST.MF</pattern></attributes>
#      <files>
#        <file>
#          <name>C:\Development\Caddying\CaddyPowerShell\2_IQToolsExample\data\dirFirst\plugins\analysis-core\META-INF\MANIFEST.MF</name>
#          <extracted>
#            <PLUGIN-VERSION>Plugin-Version: 1.11</PLUGIN-VERSION>
#            <IMPLEMENTATION-VERSION>Implementation-Version: 1.10</IMPLEMENTATION-VERSION>
#            <SHORT-NAME>Short-Name: analysis-core</SHORT-NAME>
#            <LONG-NAME>Long-Name: Static Analysis Utilities</LONG-NAME>
#          </extracted>
#        </file>
#   <file>
#          <name>C:\BuildTools\Sample.CI.BuildTools.ArtifactoryClient.DLL</name>
#          <checksum>55a442c3220a4b4564b73db47bf2995f4f</checksum>
#          <Versions>
#            <FileVersion>1.12.9999.9999</FileVersion>
#            <ProductVersion>1.12.9999.9999</ProductVersion>
#          </Versions>
#        </file>

function computeFilesDiff ([System.Xml.Linq.XElement]  $directory1 ,  [System.Xml.Linq.XElement] $directory2){
	if ($debug) { Write-Host  "-----------------------------------------------------------------------"}
	if ($debug) { Write-Host "All are ok " }
	$myFileHash=@{}
	
	Write-Output "    <files>" | out-file $outputFile -enc ascii -append
	
	#build a hash will all files in dir1
	foreach ($file in $directory1.Element("files").Elements() ) {
		$content=$file.Value
		$name=$file.Element("name").Value
		$myFileHash.$name=$content
	}
	
	foreach ($file in $directory2.Element("files").Elements() ) {
		$content=$file.Value
		$name=$file.Element("name").Value
		
		if ($debug) { Write-Host "Checking if both content are equals"}
		if ([String]::IsNullOrEmpty($myFileHash.$name)) {
			if ($debug) { Write-Host "Error : the file doesn't exist in reference file : $name" }
			#In simple mode we do not display additional stuff
			if (-not $simple) {
				Write-Output "     <added_file> $name </added_file>" | out-file $outputFile -enc ascii -append		
			}
			
		} else {			
			if ( $content.equals($myFileHash.$name)) {
				if ($debug) { Write-Host "both content are equals" }
			} else {
				if ($debug) { Write-Host "content are not equals"  } 
				$encodedkey=[system.web.httputility]::htmlencode($name)
				$encodedvalue=[system.web.httputility]::htmlencode($content)
				$encodedvalue2=[system.web.httputility]::htmlencode($($myFileHash.$name))
				Write-Output "     <different_file><name>$encodedkey</name><content1>$encodedvalue</content1><content2>$encodedvalue2</content2></different_file>" | out-file $outputFile -enc ascii -append		
				if ($debug) { Write-Host "+++++++++++" }
				if ($debug) { Write-Host "$content"}
				if ($debug) { Write-Host "+++++++++++"}
				if ($debug) { Write-Host "$($myFileHash.$name)"}
				if ($debug) { Write-Host "+++++++++++"}
			}
			$myFileHash.remove("$name")
		}
	}
	
	if ( $myFileHash.Count -gt 0 ) {
			if ($debug) { Write-Host "ERROR : SOME element not present in the second file" }
			foreach ( $key in $myFileHash.keys ) {
				if ($debug) { Write-Host "ERROR : SOME element not present : ($key) ($($myFileHash.$key))" }
				Write-Output "     <missing_file>$key</missing_file>" | out-file $outputFile -enc ascii -append
			}
	}

	Write-Output "    </files>" | out-file $outputFile -enc ascii -append
	if ($debug) { Write-Host  "-----------------------------------------------------------------------"}
	
}

# #######################################################################
#
# Compute the IQ Diff between directories
#
# #######################################################################
# <directories>    
#	<directory> ... </directory>
#	<directory> ... </directory>
#	<directory> ... </directory>
# </directories>    
function computeDirectoriesDiff ( [System.Xml.Linq.XElement]  $directories1 ,  [System.Xml.Linq.XElement] $directories2 ) {
	$myHash1=@{}
	$myHash2=@{}
	
	#First list the directory present in the first file
    if (-not ([String]::IsNullOrEmpty($directories1))) {
    	foreach ($directory in $directories1.Elements() ) {
    		$dir=$directory.Element("attributes").Element("Name").Value
    		$pattern=$directory.Element("attributes").Element("pattern").Value
    		$key="$dir$pattern"
    		$myHash1.$key=$pattern		
    		$myHash2.$key=$directory
    		if ($debug) { Write-Host "add a dir element in the hash ($dir), ($pattern)" }
    	}
    }
	
	if ($debug) { Write-Host "check the size of myHash1 , ($($myHash1.Count))" }
	
	#Second: for all directory present in the second file, check if they are present in the first hash
	# in case of error raise it
	# each time we found an element in the second list, in the first one, we remove it from the first one
	# (for step 3)
	Write-Output " <directories>" | out-file $outputFile -enc ascii -append
    if (-not ([String]::IsNullOrEmpty($directories2))) {
    	foreach ($directory in $directories2.Elements() ) {
    		$dir=$directory.Element("attributes").Element("Name").Value
    		$pattern=$directory.Element("attributes").Element("pattern").Value
    		$key="$dir$pattern"
    		if ([String]::IsNullOrEmpty($myHash1.$key)) {
    			if ($debug) { Write-Host "ERROR : ONE DIR ELEMENT NOT FOUND or different pattern: ($dir), ($pattern)" }
    			Write-Output "   <added_directory> ($dir), ($pattern) </added_directory>" | out-file $outputFile -enc ascii -append
    		} else {
    			$myHash1.remove("$key");
    			#Now, we need to check the files
    			Write-Output "   <directory>" | out-file $outputFile -enc ascii -append
    			Write-Output "     <attributes>" | out-file $outputFile -enc ascii -append
    			Write-Output "       <Name>$dir</Name>" | out-file $outputFile -enc ascii -append
    			Write-Output "       <pattern>$pattern</pattern>" | out-file $outputFile -enc ascii -append
    			Write-Output "      </attributes>" | out-file $outputFile -enc ascii -append
    			computeFilesDiff $directory $myHash2.$key
    			Write-Output "   </directory>" | out-file $outputFile -enc ascii -append
    			if ($debug) {  Write-Host "remove one element, now the size of myHash1 , ($($myHash1.Count))" }
    		}
    	}
    }
	
	#Now, if there is some elements left in the initial list, it's an error (they have not been found in the second one)
	if ($debug) { Write-Host "check the size of myHash1 , ($($myHash1.Count))" }
	if ( $myHash1.Count -gt 0 ) {
			if ($debug) { Write-Host "ERROR : SOME element not present in the second file" }
			foreach ( $key in $myHash1.keys ) {
				if ($debug) { Write-Host "ERROR : SOME element not present : ($key) ($($myHash1.$key))" }
				$value=$myHash1.$key
				$ind=$key.LastIndexOf($value)
				if ( $ind -gt 0) {
					#we have found it 
					if ($debug) { Write-Host "indice was $ind in ($key)" }
					$newValue=$key.subString(0,$ind);
				}
				if ($debug) { Write-Host "ERROR : SOME element not present : ($newValue)($($myHash1.$key))" }
				Write-Output "   <missing_directory> ($newValue), ($($myHash1.$key)) </missing_directory>" | out-file $outputFile -enc ascii -append
			}
	}
	Write-Output " </directories>" | out-file $outputFile -enc ascii -append
}

# -----------------------------------------------------------------------
#
#   BEGIN OF ANALYSE
#
# -----------------------------------------------------------------------

if ($help) {
	usage
	exit 0
}

$file1=resolve-path $file1

if ( -not (Test-Path "$file1" )) {
	fail "File $file1 doesnot exit"
}

$file2=resolve-path $file2
if ( -not (Test-Path "$file2") ) {
	fail "File $file2 doesnot exit"
}



#Load system lib
[Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.web") | Out-Null

$doc1 = [Xml.Linq.XDocument]::Load($file1)
$doc2 = [Xml.Linq.XDocument]::Load($file2)

Write-Output "<validation>" | out-file $outputFile -enc ascii


# -----------------------------------------------------------------------
#
#  Displaying META information on analysis
#
# -----------------------------------------------------------------------

$meta1 = $doc1.Element("IQCheck").Element("META")
$meta2 = $doc2.Element("IQCheck").Element("META")
writeMeta $meta1 $meta2


# -----------------------------------------------------------------------
#
#  Diff on OperatingSystem
#
# -----------------------------------------------------------------------
$OperatingSystem1 = $doc1.Element("IQCheck").Element("OperatingSystem")
$OperatingSystem2 = $doc2.Element("IQCheck").Element("OperatingSystem")
if([String]::IsNullOrEmpty($OperatingSystem1))
{
	$hashOperatingSystem1=@{}
} else {
	$hashOperatingSystem1=convertOperatingSystemToHash $OperatingSystem1
} 
if([String]::IsNullOrEmpty($OperatingSystem2))
{
	$hashOperatingSystem2=@{}
} else {
	$hashOperatingSystem2=convertOperatingSystemToHash $OperatingSystem2
} 

if ($debug) {  Write-Host "Get two hash : $($hashOperatingSystem1.Count) and $($hashOperatingSystem2.count) elements" }

computeOperatingSystemDiff $hashOperatingSystem1 $hashOperatingSystem2

# -----------------------------------------------------------------------
#
#  Diff on serverId
#
# -----------------------------------------------------------------------

$serverId1 = $doc1.Element("IQCheck").Element("serverId")
$serverId2 = $doc2.Element("IQCheck").Element("serverId")
if([String]::IsNullOrEmpty($serverId1))
{
	$hashServerId1=@{}
} else {
	$hashServerId1=convertServerIdToHash $ServerId1
} 
if([String]::IsNullOrEmpty($ServerId2))
{
	$hashServerId2=@{}
} else {
	$hashServerId2=convertServerIdToHash $ServerId2
} 

if ($debug) {  Write-Host "Get two hash : $($hashServerId1.Count) and $($hashServerId2.count) elements" }
computeServerIdDiff $hashServerId1 $hashServerId2

# -----------------------------------------------------------------------
#
#  Diff on Softwares
#
# -----------------------------------------------------------------------

$software1 = $doc1.Element("IQCheck").Element("Softwares")
$software2 = $doc2.Element("IQCheck").Element("Softwares")

if([String]::IsNullOrEmpty($software1))
{
	$hashSoftware1=@{}
} else {
	$hashSoftware1=convertSoftwaresToHash $software1		
} 
if([String]::IsNullOrEmpty($software2))
{
	$hashSoftware2=@{}
} else {
	$hashSoftware2=convertSoftwaresToHash $software2
}

if ($debug) {  Write-Host "Get two hash : $($hashSoftware1.Count) and $($hashSoftware2.count) elements" }
computeSoftDiff $hashSoftware1 $hashSoftware2

# -----------------------------------------------------------------------
#
#  Diff on Services
#
# -----------------------------------------------------------------------

$Service1 = $doc1.Element("IQCheck").Element("Services")
$Service2 = $doc2.Element("IQCheck").Element("Services")
if([String]::IsNullOrEmpty($Service1))
{
	$hashService1=@{}
} else {
	$hashService1=convertServicesToHash $Service1
} 
if([String]::IsNullOrEmpty($Service2))
{
	$hashService2=@{}
} else {
	$hashService2=convertServicesToHash $Service2
} 

if ($debug) {  Write-Host "Get two hash : $($hashService1.Count) and $($hashService2.count) elements" }
computeServicesDiff $hashService1 $hashService2

# -----------------------------------------------------------------------
#
#  Diff on Hotfixes
#
# -----------------------------------------------------------------------

$hotfixe1 = $doc1.Element("IQCheck").Element("hotfixes")
$hotfixe2 = $doc2.Element("IQCheck").Element("hotfixes")
if([String]::IsNullOrEmpty($hotfixe1))
{
	$hashHotFixe1=@{}
} else {
	$hashHotFixe1=convertHotFixesToHash $HotFixe1
} 
if([String]::IsNullOrEmpty($HotFixe2))
{
	$hashHotFixe2=@{}
} else {
	$hashHotFixe2=convertHotFixesToHash $HotFixe2
} 

if ($debug) {  Write-Host "Get two hash : $($hashHotFixe1.Count) and $($hashHotFixe2.count) elements" }
computeHotFixesDiff $hashHotFixe1 $hashHotFixe2


# -----------------------------------------------------------------------
#
#  Diff on hardware
#
# -----------------------------------------------------------------------

$hardware1 = $doc1.Element("IQCheck").Element("hardware")
$hardware2 = $doc2.Element("IQCheck").Element("hardware")

if([String]::IsNullOrEmpty($hardware1))
{
	$hashMemory1=@{}
	$hashCpu1=@{}
	$hashDisks1=@{}
} else {
	$hashMemory1=convertValueElementToHash $hardware1 "memory"
	$hashCpu1=convertValueElementToHash $hardware1 "cpu"
	$hashDisks1=convertHardwareToHashDisks $hardware1
} 
if([String]::IsNullOrEmpty($hardware2))
{
	$hashMemory2=@{}
	$hashCpu2=@{}
	$hashDisks2=@{}
} else {
	$hashMemory2=convertValueElementToHash $hardware2 "memory"
	$hashCpu2=convertValueElementToHash $hardware2 "cpu"
	$hashDisks2=convertHardwareToHashDisks $hardware2
} 

if ($debug) {  Write-Host "Disks hash : $($hashDisks1.Count) and $($hashDisks2.count) elements" }
computeHardwareDiff $hardware1 $hardware2 $hashMemory1 $hashMemory2 $hashCpu1 $hashCpu2 $hashDisks1 $hashDisks2



# -----------------------------------------------------------------------
#
#  Diff on EnvironmentVariables
#
# -----------------------------------------------------------------------

#Get the softawares section, and diff it
$Environment1 = $doc1.Element("IQCheck").Element("Environment")
$Environment2 = $doc2.Element("IQCheck").Element("Environment")
if([String]::IsNullOrEmpty($Environment1))
{
	$hashEnvironment1=@{}
} else {
	$hashEnvironment1=convertEnvironmentToHash $Environment1
}

if([String]::IsNullOrEmpty($Environment2))
{
	$hashEnvironment2=@{}
} else {
	$hashEnvironment2=convertEnvironmentToHash $Environment2
} 

if ($debug) {  Write-Host "Get two hash : $($hashEnvironment1.Count) and $($hashEnvironment2.count) elements" }
computeEnvironmentDiff $hashEnvironment1 $hashEnvironment2

# -----------------------------------------------------------------------
#
#  Diff on Directories
#
# -----------------------------------------------------------------------

#Get the directories section, and diff it
$directories1 = $doc1.Element("IQCheck").Element("directories")
$directories2 = $doc2.Element("IQCheck").Element("directories")
computeDirectoriesDiff $directories1 $directories2

# -----------------------------------------------------------------------
#
#  END of analysis, write footer
#
# -----------------------------------------------------------------------

Write-Output "</validation>" | out-file $outputFile -enc ascii -append
